package com.audaque.springboot.foshanupload.core.util;


import sun.reflect.ReflectionFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ReflectUtil {
    public static Object convertObject(Object obj, String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return clazz.cast(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            // 处理类未找到异常
        }
        return null;
    }
    public static Field getField ( final Class<?> clazz, final String fieldName ) throws Exception {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            if ( field != null )
                field.setAccessible(true);
            else if ( clazz.getSuperclass() != null )
                field = getField(clazz.getSuperclass(), fieldName);

            return field;
        }
        catch ( NoSuchFieldException e ) {
            if ( !clazz.getSuperclass().equals(Object.class) ) {
                return getField(clazz.getSuperclass(), fieldName);
            }
            throw e;
        }
    }


    public static void setFieldValue ( final Object obj, final String fieldName, final Object value ) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }


    public static Object getFieldValue ( final Object obj, final String fieldName ) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }


    public static Constructor<?> getFirstCtor ( final String name ) throws Exception {
        final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[ 0 ];
        ctor.setAccessible(true);
        return ctor;
    }


    public static <T> T createWithoutConstructor ( Class<T> classToInstantiate )
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
    }


    @SuppressWarnings ( {
            "unchecked"
    } )
    public static <T> T createWithConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes,
                                                Object[] consArgs ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
        objCons.setAccessible(true);
        Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
        sc.setAccessible(true);
        return (T) sc.newInstance(consArgs);
    }




    //接收参数时会自动装箱
    public static boolean javaBaseTypeYn(Object obj) {
        // 使用 instanceof 检查对象是否为基本数据类型的包装类
        if (obj instanceof Integer) {
            return true;
        } else if (obj instanceof Double) {
            return true;
        } else if (obj instanceof Float) {
            return true;
        } else if (obj instanceof Long) {
            return true;
        } else if (obj instanceof Short) {
            return true;
        } else if (obj instanceof Byte) {
            return true;
        } else if (obj instanceof Boolean) {
            return true;
        } else if (obj instanceof Character) {
            return true;
        } else {
            return false;
        }
    }

    //接收参数时会自动装箱
    public static boolean javaBaseTypeYn(String str) {
        // 使用 instanceof 检查对象是否为基本数据类型的包装类
        if (Objects.equals(str, "java.lang.Integer")||Objects.equals(str, "int")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Double")||Objects.equals(str, "double")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Float")||Objects.equals(str, "float")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Long")||Objects.equals(str, "long")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Short")||Objects.equals(str, "short")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Byte")||Objects.equals(str, "byte")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Boolean")||Objects.equals(str, "boolean")) {
            return true;
        } else if (Objects.equals(str, "java.lang.Character")||Objects.equals(str, "char")) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 会抛出 ClassNotFoundException 的原因是因为 java.lang.ArrayList 并非一个实际的类文件路径，它是 Java 标准库中的一个类，编译时会被加载到 JVM 中，因此不需要使用 Class.forName() 来加载。
     *
     * @param clazz
     * @return
     */
    public static Class<?> getClassByNameSkipNotFound(String clazz) {
        Class<?> aClass = null;
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            aClass = classLoader.loadClass(clazz);
        } catch (Exception e) {
            //忽略
        } finally {
            //向下执行
            try {
                if (aClass == null) {
                    aClass = Class.forName(clazz);
                } else if (aClass == null) {
                    return aClass;
                }
            } catch (Exception e) {
                //忽略
            } finally {
                //向下执行
                try {
                    if (aClass == null) {
                        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
                        aClass = classLoader.loadClass(clazz);
                    } else if (aClass == null) {
                        return aClass;
                    }
                } catch (Exception e) {
                    //忽略
                } finally {
                    //向下执行
                }
            }
        }
        return aClass;
    }

    /**
     * 获取类用到所有类
     *
     * @param aClass
     * @return
     * @throws Exception
     */
    public static List<String> getUsedClassName(Class<?> aClass,Boolean skipJavaBaseType) {
        List<Class> list = new ArrayList<>();
        loopClass(aClass, list,skipJavaBaseType);
        List<String> collect = list.stream().distinct().map(e -> e.getName()).collect(Collectors.toList());
        return collect;
    }

    public static void loopClass(Class<?> aClass, List<Class> list,Boolean skipJavaBaseType) {
        if (skipJavaBaseType&&!javaBaseTypeYn(aClass)) {
            List<Class> listAll = new ArrayList<>();
            List<Class> listLoopField = new ArrayList<>();
            List<Class> listLoopInnerClass = new ArrayList<>();
            List<Class> listLoopSupperClass = new ArrayList<>();
            List<Class> listClass = new ArrayList<>();
            loopInnerClass(aClass, listLoopInnerClass,skipJavaBaseType);
            loopSupperClass(aClass, listLoopSupperClass,skipJavaBaseType);
            listClass.add(aClass);
            listClass.addAll(listLoopInnerClass);
            listClass.addAll(listLoopSupperClass);
            for (Class item : listClass) {
                loopFieldClass(item, listLoopField,skipJavaBaseType);
            }
            listAll.addAll(listLoopInnerClass);
            listAll.addAll(listLoopSupperClass);
            listAll.addAll(listLoopField);
            //已处理的类型
            if(!list.contains(aClass)){
                list.add(aClass);
            }
            //去重
            List<Class> collect = listAll.stream().distinct().collect(Collectors.toList());
            for (Class item : collect) {
                //已经解析过的类型不用再处理
                if (!list.contains(item)) {
                    loopClass(item, list,skipJavaBaseType);
                }
            }
        }

    }


    public static void loopFieldClass(Class<?> aClass, List<Class> list,Boolean skipJavaBaseType) {
        if (skipJavaBaseType&&!javaBaseTypeYn(aClass)) {
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Class<?> type = declaredField.getType();
                list.add(type);
                List<String> collect = list.stream().distinct().map(e -> e.getTypeName()).collect(Collectors.toList());
                if (!collect.contains(type.getTypeName())) {
                    loopFieldClass(type, list,skipJavaBaseType);
                }
            }
        }

    }

    public static void loopSupperClass(Class<?> aClass, List<Class> list,Boolean skipJavaBaseType) {
        if (skipJavaBaseType&&!javaBaseTypeYn(aClass)) {
            if (aClass != null) {
                Class<?> superclass = aClass.getSuperclass();
                if (superclass != null) {
                    list.add(superclass);
                    List<String> collect = list.stream().distinct().map(e -> e.getTypeName()).collect(Collectors.toList());
                    if (!collect.contains(superclass.getTypeName())) {
                        loopSupperClass(superclass, list,skipJavaBaseType);
                    }
                }
            }
        }

    }

    public static void loopInnerClass(Class<?> aClass, List<Class> list,Boolean skipJavaBaseType) {
        if (skipJavaBaseType&&!javaBaseTypeYn(aClass)) {
            if (aClass != null) {
                Class<?>[] declaredClasses = aClass.getDeclaredClasses();
                if (declaredClasses.length > 0) {
                    List<Class> declaredClassList = Stream.of(declaredClasses).collect(Collectors.toList());
                    if (declaredClasses != null && declaredClassList.size() > 0) {
                        list.addAll(declaredClassList);
                        List<String> collect = list.stream().distinct().map(e -> e.getTypeName()).collect(Collectors.toList());
                        for (Class aClass1 : declaredClassList) {
                            if (!collect.contains(aClass1.getTypeName())) {
                                loopInnerClass(aClass1, list,skipJavaBaseType);
                            }
                        }
                    }
                }
            }
        }

    }


}
